//
//  ViewController.m
//  Runtime的使用
//
//  Created by caominglei on 2018/1/29.
//  Copyright © 2018年 caominglei. All rights reserved.
//

#import "ViewController.h"
#import <objc/message.h>
#import "Person.h"
#import "UIImage+image.h"
#import "NSObject+obj.h"
#import <ReactiveObjC.h>//面向信号
#import "MEView.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet MEView *meView;
@property (weak, nonatomic) IBOutlet UITextField *textField;
@property (strong, nonatomic) NSTimer *timer;
@property (strong, nonatomic) dispatch_source_t disTimer;

@property (assign, nonatomic) int time;
@property (strong, nonatomic) RACDisposable *disposable;
@end

@implementation ViewController
- (IBAction)btnClick:(id)sender {
    NSLog(@"me");
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //消息机制
    //[self objc_msg];
    
    /*
     交换方法
     */
    //[self exchangeMethods];
    
    //timer倒计时
//    [self gcdTimer];
    //gcd子线程 回调能来 底层已经封装了 这里需要是全局变量，否则过了就没了   不会有回调
    //GCD倒计时
//    [self dispatchTimer];
    
    //reactive timer   简单粗暴 不会有太多的问题
//    [self reactiveTimer];
   
    //场景：一个类方法非常多，加载到内存的时候也比较耗费资源，需要给每个方法生成映射表，可以动态给某个类，添加方法
    //相当于OC的懒加载,问有没有使用过performSelector 其实就是问有没有用过运行时动态添加方法
    
    
    //底层了解与使用 Target KVC  KVO  代理 通知 链式编程
    
    //reactiveobjc的实现原理：
    //1.创建信号
    //创建一个数组
    RACSubject *subject = [RACSubject subject];
    //2.订阅信号
    //创建一个订阅者O
    //将block保存到O中
    //将订阅者保存到第一步创建的数组里
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"接收到了：%@",x);
    }];
    //发送信号
    [subject sendNext:@"leader!"];
    
    
    //Target
//    [self targetSelecterDelegate];
    
    //KVC
    
    //KVO
    
    //通知 不能全部重写系统的通知 比如弹出键盘
//    [self notification];
    //重复发送验证码的按钮实现

//    [self.resendBtn addTarget:self action:@selector(resendCodingBtnClick:) forControlEvents:UIControlEventTouchUpInside];
    //某个对象的属性发送变化，就给对象的属性赋值
    [self.textField sizeToFit];
    
    RAC(_outletBtn,titleLabel.text) = self.textField.rac_textSignal;
}
- (void)resendCodingBtnClick:(UIButton *)btn
{
    self.time = 10;

    //改变状态
    btn.enabled = NO;
    //设置倒计时
    //每一秒进来都要在主线程更新UI
    _disposable = [[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
        //设置文本
        NSString *btnText = _time > 0 ? [NSString stringWithFormat:@"请等待%d秒",_time]:@"重新发送";
        //设置按钮状态
        if(_time >0){
            btn.enabled = NO;
            [btn setTitle:btnText forState:UIControlStateDisabled];
        }else{
            btn.enabled = YES;
            [btn setTitle:btnText forState:UIControlStateNormal];
            [_disposable dispose];//取消订阅
        }
        //减去时间
        _time --;//当time = 0  是否要取消订阅？
    }];
}

/**
 代理的reactive的使用
 */
- (void)targetSelecterDelegate
{
    [self.meView.subject subscribeNext:^(id  _Nullable x) {//如果是多个参数，就传一个集合
        NSLog(@"come here!");
    }];
    [[self.meView rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(RACTuple * _Nullable x) {
        NSLog(@"here:%@!",x);
    }];
}
- (void)notification
{
    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {//监听键盘的弹出
        NSLog(@"ShowNotification:%@",x);
    }];
    //监听文本输入
    [_textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
        NSLog(@"text:%@",x);
    }];
}

- (void)reactiveTimer
{
    [[RACSignal interval:1.0 onScheduler:[RACScheduler scheduler]] subscribeNext:^(NSDate * _Nullable x) {
        NSLog(@"%@=======%@",x,[NSThread currentThread]);
    }];
}
- (void)dispatchTimer
{
    _disTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
    dispatch_source_set_timer(_disTimer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);//NSEC_PER_SEC一个亿 单位：毫秒
    dispatch_source_set_event_handler(_disTimer, ^{//句柄，就是回调
        NSLog(@"----%@",[NSThread currentThread]);
        
    });
    dispatch_resume(_disTimer);
}
- (void)gcdTimer
{
    NSThread *thread = [[NSThread alloc] initWithBlock:^{
        NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(targetClicked) userInfo:nil repeats:YES];
        self.timer = timer;
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        //线程执行完后 不会回调方法 要保住它（线程）的寿命
        [[NSRunLoop currentRunLoop] run];
    }];
    [thread start];
}

- (void)targetClicked
{
    NSLog(@"Coming!");
}

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
    [self.timer invalidate];
    self.timer = nil;
}
- (void)objc_msg {
    /*
     runtime就是消息机制 xcode5后不推荐使用底层
     C与OC区别：C方法没有实现，在编译时就会报错；OC在运行时才会去methodLists方法列表里去寻找对应方法报错
     */
    Person *p = [[Person alloc] init];
    //本质使用对象 调用方法编号sel 执行方法 等价于objc_msgSend
    //...参数可以依次添加
        [p performSelector:@selector(seat)];
    objc_msgSend(p, @selector(eat));
    objc_msgSend(p, @selector(run:),123);
    
    //类对象
    Class personClass = [Person class];
    [personClass performSelector:@selector(eat)];
    objc_msgSend(personClass, @selector(eat));
    
    //分类里动态添加属性
    NSObject *obj = [[NSObject alloc] init];

    obj.name = @"123";
    NSLog(@"name:%@",obj.name);
}

- (void)exchangeMethods
{
    /*
     弊端：无法判断image，是否为nil，系统方法不够用，需要添加一个新方法，但不影响原来的方法列表；
     此时有两个办法；
     1）需要 写一个分类继承它 重写一个类方法-----本质就是交换方法
     2）使用运行时，交换方法
     */
    /*
     这个方法本质就是交换系统方法与自定义的方法，但是调用的不频繁时使用，项目过大 不合时宜
     交换是要在类开始加载的时候就交换，切只交换依次，在分类的load类方法里实现交换  实现调用后也就是调用了系统方法但是实际上却是调用了自定义的方法
     */
    [UIImage imageNamed:@"22"];
}

@end
